﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading;
using WSIMWindowsClient.WSIMProfile;
using WSIMWindowsClient.WSIMGeneral;
using WSIMWindowsClient.WSIMMessaging;
using System.ServiceModel.Security;
using System.IO;

namespace WSIMWindowsClient
{
    public partial class MainForm : Form
    {
        #region Memebers

        private Status clientStatus = new Status();
        private Dictionary<string, Thread> threadPool = new Dictionary<string, Thread>();
        private Dictionary<long, ChatForm> activeChats = new Dictionary<long, ChatForm>();
        private WSIMGeneralOperationsClient wsGeneral = ObjectFactory.GetGeneralWS();
        private WSIMProfileOperationsClient wsProfile = ObjectFactory.GetProfileWS();
        private WSIMMessagingOperationsClient wsMessaging = ObjectFactory.GetMessagingWS(false);
        private Dictionary<long, string> myContacts = new Dictionary<long, string>();
        private string receivedPath;

        public string ReceivedPath
        {
            get { return receivedPath; }
            set { receivedPath = value; }
        }

        #endregion

        #region Delegates

        private PopulateList methodToInvokeToManageContactsList;
        private ManageMessages methodToInvokeToManageMessages;

        private delegate void PopulateList(UserList list);
        private delegate void ManageMessages(List<WSIMMessaging.Message> list);

        #endregion    

        public MainForm()
        {
            InitializeComponent();
            CheckForIllegalCrossThreadCalls = false;


            receivedPath = Environment.GetFolderPath(Environment.SpecialFolder.Personal) 
                + @"\" +global::WSIMWindowsClient.Properties.Settings.Default.receivedPath;

            if (!Directory.Exists(receivedPath))
            {
                Directory.CreateDirectory(receivedPath);
            }


            #region Events and Handlers

            methodToInvokeToManageContactsList = new PopulateList(PopulateListWithLINQ);
            methodToInvokeToManageMessages = new ManageMessages(ManageNewMessages);
            this.FormClosing += new FormClosingEventHandler(MainForm_FormClosing);
            this.contactTree.DoubleClick += new EventHandler(contactTree_DoubleClick);
            this.txtPassword.Validating += new CancelEventHandler(login_validation);
            

            #endregion
             
        }

        #region Validation e Control Code

        internal Status ClientStatus
        {
            get { return clientStatus; }
            set { clientStatus = value; }
        }

        private void addContactToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (!clientStatus.LoggedIn) return;
            UserSearch se = new UserSearch();
            se.MdiParent = this;
            se.Show();
        }

        private void deleteContactToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (contactTree.SelectedNode != null)
                ObjectFactory.GetProfileWS().DeleteContact(new DeleteContactRequest(Int64.Parse(contactTree.SelectedNode.Name)));
            RefreshUserList();
        }

        void login_validation(object sender, CancelEventArgs e)
        {
            if (String.IsNullOrEmpty(txtPassword.Text)){
                error.SetError(txtPassword, "Insert Password!");
                e.Cancel = true;
            }
            if (String.IsNullOrEmpty(txtUsername.Text))
            {
                e.Cancel = true;
                error.SetError(txtUsername, "Insert Username!");
            }
        }

        
        void contactTree_DoubleClick(object sender, EventArgs e)
        {
            StartConversation(contactTree.SelectedNode.Name,contactTree.SelectedNode.Text);
        }

        void MainForm_FormClosing(object sender, FormClosingEventArgs e)
        {
            if (clientStatus.LoggedIn) StopConnection();
        }

        private void registerToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Form f = new Registration();
            f.MdiParent = this;
            f.Show();
        }

        private void loginToolStripMenuItem_Click(object sender, EventArgs e)
        {
            StartLoginProcedures();
            UpdateMenuAndStatus();            
        }
        
        private void logoutToolStripMenuItem_Click(object sender, EventArgs e)
        {
            StopConnection();
            UpdateMenuAndStatus();
        }

        private void cmdSaveConnect_Click(object sender, EventArgs e)
        {
            SaveProperties();
            ObjectFactory.UpdateCredentials(txtUsername.Text, txtPassword.Text);
        }

        #endregion

        #region Business Methods

        private void StartConversation(string fellow,string nick) {
            long id = Int64.Parse(fellow);
            if (!activeChats.ContainsKey(id))
            {
                ChatForm frm = new ChatForm(id,nick, this);
                activeChats.Add(id, frm);
            }
        }

        private void PopulateListWithLINQ(UserList list)
        {
            contactTree.Nodes.Clear();
            //Estrazione gruppi e utenti con LINQ raggruppando per gruppi etc
            var groups =
                (from d in list.Users
                orderby d.Group
                select d.Group).Distinct();

            foreach (var i in groups) {
                TreeNode node = new TreeNode(i);
                var subUsers =
                    from d in list.Users
                    where d.Group == i
                    select d;
                foreach (var j in subUsers)
                {
                    node.Nodes.Add(j.UserID.ToString(), j.Nickname, (j.Online ? 1 : 2), (j.Online ? 1 : 2));
                    myContacts.Add(j.UserID, j.Nickname);
                }                
                contactTree.Nodes.Add(node);
            }
            
            
        }

        private void StartLoginProcedures()
        {
            long id;
            try
            {
                id= wsGeneral.IsValid(new IsValidRequest()).IsValidResult;
            }
            catch (MessageSecurityException) {
                MessageBox.Show("Invalid credentials!");
                return;
            }
            clientStatus.LoggedIn = true;
            clientStatus.UserId = id;

            //lancia thread per ricezione messaggi
            Thread t = new Thread(UserListThread);
            threadPool.Add("userList",t);
            //lancia thread per aggiornamento lista utenti
            Thread t2 = new Thread(MessagesThread);
            threadPool.Add("messages",t2);

            t.Start();
            t2.Start();
            

            ClientStatus.LoggedIn = true;
        }

        private void UpdateMenuAndStatus()
        {
            loginToolStripMenuItem.Enabled = !ClientStatus.LoggedIn;
            logoutToolStripMenuItem.Enabled = ClientStatus.LoggedIn;            
        }

        private void StopConnection()
        {
            foreach (Thread t in threadPool.Values)
            {                
                t.Abort();
                t.Interrupt();                
            }

            threadPool = new Dictionary<string, Thread>();
            wsGeneral.UserLeave(new UserLeaveRequest());
            contactTree.Nodes.Clear();

            ClientStatus.LoggedIn = false;
        }

        private void ManageNewMessages(List<WSIMWindowsClient.WSIMMessaging.Message> messages)
        {
            foreach (WSIMMessaging.Message msg in messages)
            {
                if (activeChats.ContainsKey(msg.Sender))
                {
                    activeChats[msg.Sender].NewMessage(msg);
                }
                else
                {
                    string nick=msg.Sender.ToString();
                    if (myContacts.ContainsKey(msg.Sender)) nick = myContacts[msg.Sender]; 
                    ChatForm frm = new ChatForm(msg.Sender,nick, this);
                    activeChats.Add(msg.Sender, frm);
                    frm.NewMessage(msg);
                }
            }
        }

        private void SaveProperties()
        {
            global::WSIMWindowsClient.Properties.Settings.Default.wsimUsername = txtUsername.Text;
            global::WSIMWindowsClient.Properties.Settings.Default.wsimPassword = txtPassword.Text;
            global::WSIMWindowsClient.Properties.Settings.Default.Save();
        }

        public void RefreshUserList() {
            threadPool["userList"].Interrupt();
        }

        #endregion

        #region Threading

        private void UserListThread() {
            try
            {
                while (true)
                {
                    try
                    {
                        GenerateUserListResponse resp = wsProfile.GenerateUserList(new GenerateUserListRequest());
                        UserList list = resp.GenerateUserListResult;
                        BeginInvoke(methodToInvokeToManageContactsList, new object[] { list });
                        Thread.Sleep(Int32.Parse(global::WSIMWindowsClient.Properties.Settings.Default.userListRefreshRate));
                    }
                    catch (ThreadInterruptedException)
                    {
                        continue;
                    }
                }
            }
            catch (ThreadAbortException) { 
                //Ottimo
            }
            catch (Exception ex)
            {
                throw new Exception("", ex);
                //TODO: log 
            }         
        }

        private void MessagesThread() {
            try
            {
                while (true)
                {
                    if (clientStatus.FirstTime)
                    {
                        Thread.Sleep(4000);
                        clientStatus.FirstTime = false;
                    }

                    try
                    {
                        GetNewMessagesResponse resp = wsMessaging.GetNewMessages(new GetNewMessagesRequest());
                        List<WSIMMessaging.Message> messages = resp.GetNewMessagesResult;
                        BeginInvoke(methodToInvokeToManageMessages, new object[] { messages });
                        //TODO: gestire lato server il wait..
                        Thread.Sleep(2000);
                    }
                    catch (Exception ex) {
                        //wsMessaging = ObjectFactory.GetMessagingWS(true);
                        continue;
                    }

                }
            }
            catch (ThreadAbortException)
            {
                //TODO: log 
            }
            catch (Exception) {
                
            }

        }

        #endregion

        private void cmdLogin_Click(object sender, EventArgs e)
        {
            loginToolStripMenuItem_Click(sender, e);
        }

        private void receivedFolderToolStripMenuItem_Click(object sender, EventArgs e)
        {            
            System.Diagnostics.Process.Start(receivedPath);
        }

    }
}
